package dam

import (
	"sync/atomic"
	"time"

	"go-main/library/log"
	"go-main/library/rate/limit"
	"go-main/library/rate/limit/bbr"
	"go-main/library/stat/metric"
)

var (
	_metricServerBBR = metric.NewCounterVec(&metric.CounterVecOpts{
		Namespace: serverNamespace,
		Subsystem: "",
		Name:      "bbr_total",
		Help:      "http server bbr total.",
		Labels:    []string{"url"},
	})
)

// RateLimiter bbr middleware.
type RateLimiter struct {
	group   *bbr.Group
	logTime int64
}

// NewRateLimiter return a ratelimit middleware.
func NewRateLimiter(conf *bbr.Config) (s *RateLimiter) {
	return &RateLimiter{
		group:   bbr.NewGroup(conf),
		logTime: time.Now().UnixNano(),
	}
}

func (b *RateLimiter) printStats(routePath string, limiter limit.Limiter, allow bool) {
	now := time.Now().UnixNano()
	span := time.Second * 3
	lf := log.Infof
	if !allow {
		span = time.Millisecond * 10
		lf = log.Warnf
	}
	if now-atomic.LoadInt64(&b.logTime) > int64(span) {
		atomic.StoreInt64(&b.logTime, now)
		lf("http.bbr path:%s allow:%t stat:%+v", routePath, allow, limiter.(*bbr.BBR).Stat())
	}
}

// Limit return a bm handler func.
func (b *RateLimiter) Limit() HandlerFunc {
	return func(c *Context) {
		limiter := b.group.Get(c.RoutePath)
		done, err := limiter.Allow(c)
		b.printStats(c.RoutePath, limiter, err == nil)
		if err != nil {
			routePath := c.RoutePath
			if routePath == "" {
				routePath = "/"
			}
			_metricServerBBR.Inc(routePath[1:])
			c.JSON(nil, err)
			c.Abort()
			return
		}
		defer func() {
			done(limit.DoneInfo{Op: limit.Success})
		}()
		c.Next()
	}
}
